
package dieci.ui.handlers;

import dieci.exceptions.StockNotAvailableException;
import dieci.managers.rawmaterial.RawMaterialProductManager;
import dieci.managers.rawmaterial.RawMaterialStockManager;
import dieci.managers.salesmanager.SalesManager;
import dieci.managers.stockmanager.ProductManager;
import dieci.managers.stockmanager.StockManager;
import dieci.system.Product;
import dieci.system.ProductStock;
import dieci.system.RawMaterial;
import dieci.system.RawMaterialProduct;
import dieci.system.RawMaterialStock;
import dieci.system.Sale;
import dieci.system.SaleDetails;
import dieci.utils.Constants;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author DuckSys
 */
public class UISaleHandler {

    private SalesManager salesManager;
    private StockManager stockManager;
    private ProductManager productManager;
    private RawMaterialStockManager rawMaterialStockManager;
    private RawMaterialProductManager rawMaterialProductManager;
    
    public UISaleHandler ()
    {
        salesManager = new SalesManager();
        stockManager = new StockManager();
        productManager = new ProductManager();
        rawMaterialProductManager = new RawMaterialProductManager();
        rawMaterialStockManager = new RawMaterialStockManager();
    }
        
    public DefaultTableModel getSalesListModel()
    {
        List<Sale> sales = salesManager.list();
        DefaultTableModel dm = new DefaultTableModel()
        {
            @Override
            public boolean isCellEditable(int row, int col)
            {
                return false;
            }
        };
        
        String col[] = {"Nro Venta","Fecha","Cliente","Monto Total","Cobrada","Entregada"};
        dm.setColumnIdentifiers(col);
        
        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
        
        //get data from rows
        for (Sale sale : sales) {
            dm.addRow(new Object[]{sale.getSaleId(),df.format(sale.getSaleDate()),
                sale.getClient(),sale.getTotalSaleAmount(), (sale.isCharged())?"SI":"NO", (sale.isDelivered())?"SI":"NO"});        
        }
        
        return dm;
    }
    
    public void createOrSaveSale(Sale sale, DefaultTableModel productSaleDetails) throws Exception
    {
        //CREATE OR SAVE
        try {
            Map<Long, Integer> productsAmountToSell = new HashMap<>();
            if (sale != null) {
                if (sale.getClient().getClientId() == 0) {
                    throw new Exception("Debe ingresar el cliente");
                }
                else if(sale.getDetails().isEmpty()) {
                    throw new Exception("Debe seleccionar al menos un producto ");
                }
                else 
                {
                    //this is used for updates
                    Sale originalSale = salesManager.find(sale.getSaleId());
                    Map<Long,Integer> originalProductsBought = getOriginalProductsBought(originalSale);
                    
                    StringBuffer prodNotAvailable = validateStock(productSaleDetails.getDataVector(),
                            originalProductsBought);
                                        
                    if (prodNotAvailable.length() == 0) {
                        if (originalSale != null) {
                            //it's an update. Delete the sale and create a new sale.
                            salesManager.deleteSale(originalSale);
                            updateProductStockForSale(originalSale.getDetails(), Constants.DELETE_OPERATION);
                            sale.setSaleId(0);
                        }
                        salesManager.saveSale(sale);
                    }
                    else
                    {
                        String message = prodNotAvailable.substring(0, prodNotAvailable.length()-2);
                        throw new StockNotAvailableException("No hay stock disponible para el/los productos "+message);                        
                    }
                    
                }
                
                updateProductStockForSale(sale.getDetails(), Constants.ADD_OPERATION);
                updateRawMaterialStockForSale(sale.getDetails()); 
            }
        }
        catch (Exception e2) {
           throw e2;
        }
    }

    public void DeleteSale(long saleId)
    {
        Sale sale = salesManager.find(saleId);

        if (sale != null) 
        {
            salesManager.deleteSale(sale);
            updateProductStockForSale(sale.getDetails(), Constants.DELETE_OPERATION);
        }
    }
    
    public Sale findSale(long id)
    {
        return salesManager.find(id);
    }
        
    
    private StringBuffer validateStock(Vector productsAmountToSell, Map<Long,Integer> originalQuantityBought)
    {
        StringBuffer productsNotAvailable = new StringBuffer();
        
        for (Iterator it = productsAmountToSell.iterator(); it.hasNext();) {
            Vector object = (Vector) it.next();
            Product product = (Product) object.get(0);
            int bought = 0;
            if(!originalQuantityBought.isEmpty()){
                bought = originalQuantityBought.get(product.getProductId());
            }
            int amountToSell = Integer.valueOf((String) object.get(1));
            
            if (!stockManager.isStockAvailableForProduct(product, amountToSell)) {
                if(amountToSell > bought){
                    productsNotAvailable.append(product.getProductCode() + ", ");
                }                    
            }            
        }
               
        return productsNotAvailable;
    }
    
    
    
    private void updateProductStockForSale(Set<SaleDetails> saleDetails, String operation)
    {
        for (SaleDetails saleDetail : saleDetails) {
            
            ProductStock stock = stockManager.findByProductId(saleDetail.getProduct().getProductId());
                            
            if (stock != null) {
                int saleAmountToUpdate = saleDetail.getAmount();
                int availableAmount = stock.getAvailableAmount();
                int totalAmount = stock.getTotalAmount();
                
                if(operation.equals(Constants.ADD_OPERATION))
                {
                    availableAmount-= saleAmountToUpdate;
                    totalAmount-= saleAmountToUpdate;
                }
                else if(operation.equals(Constants.DELETE_OPERATION))
                {
                    availableAmount+= saleAmountToUpdate;
                    totalAmount+= saleAmountToUpdate;
                }
                
                stock.setAvailableAmount(availableAmount);
                stock.setTotalAmount(totalAmount);
            }
            else{
                stock = new ProductStock(saleDetail.getProduct(), saleDetail.getAmount(), 
                        0, saleDetail.getAmount());
            }
            
            stockManager.saveProductStock(stock);
        }
    }

    private Map<Long, Integer> getOriginalProductsBought(Sale sale)
    {
        Map<Long, Integer> originalStock = new HashMap<>();
        if (sale != null) {
            Set<SaleDetails> details = sale.getDetails();
            for (SaleDetails saleDetail : details) {
                originalStock.put(saleDetail.getProduct().getProductId(), saleDetail.getAmount());
            }
        }
        return originalStock;
    }

    private void updateRawMaterialStockForSale(Set<SaleDetails> details) {
        for (SaleDetails saleDetail : details) 
        {
            List<RawMaterialProduct> rawMaterialsProduct = rawMaterialProductManager.findByProduct(saleDetail.getProduct());
            for (RawMaterialProduct rawMaterialProduct : rawMaterialsProduct) 
            {
                RawMaterialStock stock = rawMaterialStockManager.findByRawMaterialId(rawMaterialProduct.getRawMaterial().getRawMaterialId());
                if(stock != null)
                {
                    double newAmount = stock.getTotalAmount() - rawMaterialProduct.getAmount();
                    if(newAmount < 0)
                    {
                        newAmount = 0;
                    }
                    stock.setTotalAmount(newAmount);
                    rawMaterialStockManager.saveRawMaterialStock(stock);
                }
            }
        }
    }
    
}
